/**
 *
 */
package edu.nps.ai.astar;

import java.util.Collection;

import org.apache.commons.lang.builder.CompareToBuilder;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;

/**
 * @author Tim
 *
 */
public class Point implements Comparable<Point>{
	/**
	 * Holds the x coordinate
	 */
	private Integer x;
	/**
	 * Holds the x coordinate
	 */
	private Integer y;
	/**
	 * Holds the x coordinate
	 */
	private Integer z;
	/**
	 * Holds the name
	 */
	private String name;

	/**
	 * Holds the successor list
	 */
	private BiMap<String, Point> successorPointMap;

	/**
	 * @param x
	 * @param y
	 * @param z
	 * @param name
	 */
	public Point(Integer x, Integer y, Integer z, String name) {
		if (x == null || y == null || z == null) {
			throw new IllegalArgumentException("The x, y, z coordinates and name are required");
		}
		this.x = x;
		this.y = y;
		this.z = z;
		this.name = name;
		this.successorPointMap = HashBiMap.create(4);
	}

	/**
	 * @return the x
	 */
	public Integer getX() {
		return x;
	}

	/**
	 * @param x
	 *            the x to set
	 */
	public void setX(final Integer x) {
		this.x = x;
	}

	/**
	 * @return the y
	 */
	public Integer getY() {
		return y;
	}

	/**
	 * @param y
	 *            the y to set
	 */
	public void setY(final Integer y) {
		this.y = y;
	}

	/**
	 * @return the z
	 */
	public Integer getZ() {
		return z;
	}

	/**
	 * @param z
	 *            the z to set
	 */
	public void setZ(final Integer z) {
		this.z = z;
	}

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name
	 *            the name to set
	 */
	public void setName(final String name) {
		this.name = name;
	}

	/**
	 * Add a node to the successor point map
	 * @param operator The operator aft, port, starboard, forward
	 * @param nodeName The name of the node
	 */
	public void addSuccessorPointToMap(final String operator, final Point point) {
		if (operator == null || point == null) {
			throw new IllegalArgumentException("The operator and point are required");
		}
		if (successorPointMap == null) {
			throw new IllegalStateException("The successor point map is required");
		}
		successorPointMap.put(operator, point);
	}

	/**
	 * Get the list of successor nodes
	 * @return The list of successor nodes
	 */
	public Collection<Point> getSuccessorPointList() {
		return successorPointMap.values();
	}

	/**
	 * Helper method to print the operations to the node
	 * @param successorPoint The successor point
	 * @return The operator
	 */
	public String getOperatorForPoint(Point successorPoint) {
		BiMap<Point, String> inverseMap = successorPointMap.inverse();
		return inverseMap.get(successorPoint);
	}

	/**
	 * Implementation of the toString method
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return name + " (" + x + "," +  y + "," + z + ")";
	}

	/**
	 * Implementation of the hash code method
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		HashCodeBuilder hashCodeBuilder = new HashCodeBuilder(1, 31);
		hashCodeBuilder.append(this.x);
		hashCodeBuilder.append(this.y);
		hashCodeBuilder.append(this.z);
		hashCodeBuilder.append(this.name);
		return hashCodeBuilder.toHashCode();
	}

	/**
	 * Implementation of the is equals method
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (null == obj) {
			return false;
		}
		if (obj instanceof Point == false) {
			return false;
		}
		if (this == obj) {
			return true;
		}
		Point comparePoint = (Point) obj;
		EqualsBuilder equalsBuilder = new EqualsBuilder();
		equalsBuilder.append(this.x, comparePoint.getX());
		equalsBuilder.append(this.y, comparePoint.getY());
		equalsBuilder.append(this.z, comparePoint.getZ());
		equalsBuilder.append(this.name, comparePoint.getName());
		return equalsBuilder.isEquals();
	}

	/**
	 * Implementation of compareTo
	 */
	public int compareTo(Point comparePoint) {
		CompareToBuilder compareToBuilder = new CompareToBuilder();
		compareToBuilder.append(this.x, comparePoint.getX());
		compareToBuilder.append(this.y, comparePoint.getY());
		compareToBuilder.append(this.z, comparePoint.getZ());
		compareToBuilder.append(this.name, comparePoint.getName());
		return compareToBuilder.toComparison();
	}
}
